#pragma once
#include <vector>

//Behaviortrees classes are defined in this file. A complete behavior tree just runs a node class

//walks all the nodes to avoid having to build up recursion. Allows for a fast exit and reentetry and soon as the goal is found.


//Abstaction interface
class Agent;
class BunnyAgent;
class WolfAgent;
class BearAgent;
class BNode
{

public:
	
	enum Status { Succeded, Failed, Running };
	//For checking the status several times.
	Status m_eStatus;
	//run and set parent as current node

	//Run function runs the function of a node in the behavior tree
	virtual int Run() = 0;
};

//Holds other nodes as lists

class CompositeBNode : public BNode
{
protected:
	//Last running records the last running node's iterator. This allows it to jump to that node without going into the prerequnces
	//Example: A sequncer has three subnodes, A,B,C. It runs A, then returns, then starts B. However, traversing down again
	//It goes to A and restarts it. With this, the Sequncer will never finish B, and never reach C. This prevents that
	//By jumping straight to B untill it's finished, where it goes to C.
	int m_LastRunning = -1;
	std::vector<BNode*> m_xChildNodes;

public:
	void AddChildNode(BNode* node){ 
		node->m_eStatus = Status::Failed;
		m_xChildNodes.push_back(node); };
	std::vector<BNode*> GetChildren(){ return m_xChildNodes; };

};

//the selector selects one of the base nodes and makes into a node	
class SelectorBNode : public CompositeBNode
{

	 int Run()
	{
		 int addToIterator = 0;
		 if (m_LastRunning != -1)
		 {
			 addToIterator = m_LastRunning + 1;
		 }

		 for (int i = 0 + addToIterator; i < m_xChildNodes.size(); i++)
		{
			m_xChildNodes[i]->Run();

			if (m_xChildNodes[i]->m_eStatus == Status::Running)
			{
				m_eStatus = Status::Running;
				return m_eStatus;
			}
			 else if(m_xChildNodes[i]->m_eStatus == Status::Succeded)
			{
				 m_eStatus = Status::Succeded;
				 return m_eStatus;
			}
		}
		m_eStatus = Status::Succeded;
		return  m_eStatus;
	}




};

class SequencerBNode : public CompositeBNode
{
	int Run()
	{

		int addToIterator = 0;
		if (m_LastRunning != -1)
		{
			addToIterator = m_LastRunning + 1;
		}

		for (int i = 0 + addToIterator; i < m_xChildNodes.size(); i++)
		{

			m_xChildNodes[i]->Run();

			if (m_xChildNodes[i]->m_eStatus == Status::Running)
			{
				m_eStatus = Status::Running;
				return m_eStatus;
			}
			else if (m_xChildNodes[i]->m_eStatus == Status::Failed)
			{
				m_eStatus = Status::Failed;
				return  m_eStatus;
			}
		}
		m_eStatus = Status::Succeded;
		return m_eStatus;
	}



};

//To comprehend this:
//Agent has a function called foo.
//AgentActionBNode stores foo, and agent.
//When called, it runs foo on agent. foo must return an int (enum).
//This means behaviors are ultimatly stored in the class they exsist in. 
//BunnyAgent can have behaviors stored in BunnyAgent.h for example.

class AgentActionBnode : public BNode
{
	
public:
	int Run(){ m_eStatus = (Status)(m_xAgent->*FunctionPointer)(m_eStatus); };
	void SetAgent(Agent* agent){ m_xAgent = agent; };
	Agent* GetAgent(){ return m_xAgent; };

	int(Agent::*FunctionPointer)(Status);

private:
	Agent* m_xAgent;

	
};

//It does nesscitate some extra actionBnode classes for each agent.
//One could put all their behaviors in Agent of course and make them generic.
class BunnyAgentActionBnode : public BNode
{

public:
	int Run(){ 
		m_eStatus =(Status) (m_xAgent->*FunctionPointer)(m_eStatus);
		return m_eStatus;	};
	void SetAgent(BunnyAgent* agent){ m_xAgent = agent; };
	BunnyAgent* GetAgent(){ return m_xAgent; };

	int(BunnyAgent::*FunctionPointer)(Status);

private:
	BunnyAgent* m_xAgent;


};

// Wolf
class WolfAgentActionBnode : public BNode
{
	public:
		int Run(){
			m_eStatus = (Status)(m_xAgent->*FunctionPointer)(m_eStatus);
			return m_eStatus;
		};

		void SetAgent(WolfAgent* agent){ m_xAgent = agent; };
		WolfAgent* GetAgent(){ return m_xAgent; };

		int(WolfAgent::*FunctionPointer)(Status);

	private:
		WolfAgent* m_xAgent;
};

// Bear
class BearAgentActionBnode : public BNode
{
public:
	int Run(){
		m_eStatus = (Status)(m_xAgent->*FunctionPointer)(m_eStatus);
		return m_eStatus;
	};

	void SetAgent(BearAgent* agent){ m_xAgent = agent; };
	BearAgent* GetAgent(){ return m_xAgent; };

	int(BearAgent::*FunctionPointer)(Status);

private:
	BearAgent* m_xAgent;
};